package com.chatplus.application.controller.api;

import cn.dev33.satoken.annotation.SaIgnore;
import com.chatplus.application.common.logging.SouthernQuietLogger;
import com.chatplus.application.common.logging.SouthernQuietLoggerFactory;
import com.chatplus.application.domain.dto.AdminConfigDto;
import com.chatplus.application.service.wxmp.impl.WeChatMpService;
import com.chatplus.application.util.ConfigUtil;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
import org.springframework.web.bind.annotation.*;

import java.io.BufferedReader;
import java.io.IOException;
import java.nio.charset.StandardCharsets;

/**
 * 微信公众号相关接口
 *
 * @author chj
 * @date 2024/4/1
 **/
@RequestMapping("/api/wechatMp")
@RestController
public class WechatMpController {
    static final SouthernQuietLogger LOGGER = SouthernQuietLoggerFactory.getLogger(WechatMpController.class);

    private final WeChatMpService weChatMpService;


    public WechatMpController(WeChatMpService weChatMpService) {
        this.weChatMpService = weChatMpService;
    }

    /**
     * 构建网页授权链接
     */
    @GetMapping("/getOauthUrl")
    @SaIgnore
    public String getOauthUrl() {
        AdminConfigDto adminConfigDto = ConfigUtil.getAdminConfig();
        String authUrl = weChatMpService.getOAuth2Service().buildAuthorizationUrl(
                adminConfigDto.getBaseBackendUrl() + "/api/social/wechatMpCallback",
                WxConsts.OAuth2Scope.SNSAPI_USERINFO, null);
        LOGGER.message("构建网页授权链接").context("authUrl", authUrl).info();
        return authUrl;
    }

    /*
     * 对接 API，注意返回类型为void，不能为String。原样返回的数据需要直接使用HttpServletResponse
     * 微信官方说明：https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Access_Overview.html
     * 微信公众号接入校验
     * @param signature 微信加密签名，signature结合了开发者填写的 token 参数和请求中的 timestamp 参数、nonce参数。
     * @param timestamp 时间戳
     * @param nonce     这是个随机数
     * @param echostr   随机字符串，验证成功后原样返回
     */
    @GetMapping(value = "notify")
    @SaIgnore
    public void get(@RequestParam(required = false) String signature,
                    @RequestParam(required = false) String timestamp,
                    @RequestParam(required = false) String nonce,
                    @RequestParam(required = false) String echostr,
                    HttpServletResponse response) throws IOException {
        if (!this.weChatMpService.checkSignature(timestamp, nonce, signature)) {
            LOGGER.message("接收到了未通过校验的微信消息，这可能是token配置错了，或是接收了非微信官方的请求")
                    .context("signature", signature)
                    .context("timestamp", timestamp)
                    .context("nonce", nonce)
                    .context("echostr", echostr)
                    .warn();
            return;
        }
        response.setCharacterEncoding("UTF-8");
        response.getWriter().write(echostr);
        response.getWriter().flush();
        response.getWriter().close();
    }

    /**
     * 当设置完微信公众号的接口后，微信会把用户发送的消息，扫码事件等推送过来
     *
     * @param signature    微信加密签名，signature结合了开发者填写的 token 参数和请求中的 timestamp 参数、nonce参数。
     * @param encType      加密类型（暂未启用加密消息）
     * @param msgSignature 加密的消息
     * @param timestamp    时间戳
     * @param nonce        随机数
     */
    @PostMapping(value = "notify", produces = "text/xml; charset=UTF-8")
    @SaIgnore
    public void notify(HttpServletRequest httpServletRequest,
                       HttpServletResponse httpServletResponse,
                       @RequestParam("signature") String signature,
                       @RequestParam(name = "encrypt_type", required = false) String encType,
                       @RequestParam(name = "msg_signature", required = false) String msgSignature,
                       @RequestParam("timestamp") String timestamp,
                       @RequestParam("nonce") String nonce) throws IOException {
        if (!this.weChatMpService.checkSignature(timestamp, nonce, signature)) {
            LOGGER.message("接收到了未通过校验的微信消息，这可能是token配置错了，或是接收了非微信官方的请求")
                    .context("signature", signature)
                    .context("encType", encType)
                    .context("msgSignature", msgSignature)
                    .context("timestamp", timestamp)
                    .context("nonce", nonce)
                    .warn();
            return;
        }
        BufferedReader bufferedReader = httpServletRequest.getReader();
        String str;
        StringBuilder requestBodyBuilder = new StringBuilder();
        while ((str = bufferedReader.readLine()) != null) {
            requestBodyBuilder.append(str);
        }
        String requestBody = requestBodyBuilder.toString();
        LOGGER.message("接收微信请求")
                .context("signature", signature)
                .context("encType", encType)
                .context("msgSignature", msgSignature)
                .context("timestamp", timestamp)
                .context("nonce", nonce)
                .context("requestBody", requestBody)
                .info();
        WxMpXmlMessage inMessage2 = WxMpXmlMessage.fromXml(requestBody);
        LOGGER.message("接收到请求消息").context("事件", inMessage2.getEventKey()).info();
        String out = null;
        if (encType == null) {
            // 明文传输的消息
            WxMpXmlMessage inMessage = WxMpXmlMessage.fromXml(requestBody);
            WxMpXmlOutMessage outMessage = this.weChatMpService.route(inMessage);
            if (outMessage == null) {
                httpServletResponse.getOutputStream().write(new byte[0]);
                httpServletResponse.flushBuffer();
                httpServletResponse.getOutputStream().close();
                return;
            }
            out = outMessage.toXml();
        } else if ("aes".equals(encType)) {
            // aes加密的消息
            WxMpXmlMessage inMessage = WxMpXmlMessage.fromEncryptedXml(requestBody,
                    this.weChatMpService.getWxMpConfigStorage(), timestamp, nonce, msgSignature);
            LOGGER.message("消息解密后内容").context("message", inMessage.getEventKey()).info();
            WxMpXmlOutMessage outMessage = this.weChatMpService.route(inMessage);
            if (outMessage == null) {
                httpServletResponse.getOutputStream().write(new byte[0]);
                httpServletResponse.flushBuffer();
                httpServletResponse.getOutputStream().close();
                return;
            }
            out = outMessage.toEncryptedXml(this.weChatMpService.getWxMpConfigStorage());
        }
        LOGGER.message("组装回复信息").context("out", out).info();
        if (out == null) {
            httpServletResponse.getOutputStream().write(new byte[0]);
        } else {
            httpServletResponse.getOutputStream().write(out.getBytes(StandardCharsets.UTF_8));
        }
        httpServletResponse.flushBuffer();
        httpServletResponse.getOutputStream().close();
    }
}
